<template>
    <div>
        <input type="file" multiple ref="inputRef" @change="methods.inputFile" class="c-hide" :accept="props.accept" />

        <a-button v-show="canAdd && !formItemHook.myDisabled" :color="formItemHook.myColor" :size="formItemHook.mySize" :sharp="formItemHook.mySharp" :disabled="formItemHook.myDisabled" :class="[{ 'c-m-b-m': items.length > 0 }]" @click="methods.openFileSelector">
            <span class="fa fa-cloud-upload"></span>
            <span>　{{ $t("AInputImageMulti.SelectImages") }}</span>
        </a-button>

        <a-table :headerList="headerList" :dataList="items" :selectable="false" :isInner="true" :draggable="!formItemHook.myDisabled" @drag-end="methods.onDragEnd" v-if="items.length > 0" ref="table">
            <template v-slot:data-thumbnail="{ data }">
                <a-fitimg v-if="data.src" :src="data.src" @info="methods.setInfo(data.id, $event)" style="width: 100px; height: 100px">
                </a-fitimg>
            </template>

            <template v-slot:data-size="{ data }">
                {{ methods.getSizeStr(data.size) }}
            </template>

            <template v-slot:data-status="{ data }">
                {{ statusList[data.status] }}
            </template>

            <template v-slot:data-operations="{ data }">
                <a-control-group>
                    <a-button color="error" :disabled="formItemHook.myDisabled" @click="methods.removeItem(data.id)">{{ $t("AInputImageMulti.Delete") }}</a-button>
                    <a-button v-if="data.status === 2" color="default" @click="methods.upload(data.id)">{{ $t("AInputImageMulti.Retry") }}</a-button>
                </a-control-group>
            </template>
        </a-table>
    </div>
</template>

<script setup lang="ts">
import { InputPropsType, Rule_maxLength, Rule_minLength } from '@/types'
import AButton from './AButton.vue'
import AControlGroup from './AControlGroup.vue'
import AFitimg from './AFitimg.vue'
import ATable from './ATable.vue'
import utils from '@/common/utils'
import { computed, onMounted, reactive, watch, ref } from 'vue'
import { useFormItem } from '@/hooks/useFormItem'
import { $t } from '@/lang'
import { useInput } from '@/hooks/useInput'

interface PropsType extends InputPropsType<string[] | undefined, Rule_minLength | Rule_maxLength> {
    getSrcFunc?: (id: string) => string
    accept?: string
    uploadFunc?: (file: File) => string | Promise<string>
    minWidth?: number
    maxWidth?: number
    minHeight?: number
    maxHeight?: number
    minSize?: number
    maxSize?: number
    validateImgFunc?: (width: number, height: number, size: number) => boolean
}
const props = withDefaults(defineProps<PropsType>(), {
    value: undefined,
    color: undefined,
    disabled: undefined,
    size: undefined,
    sharp: undefined,
    rules: () => [],
    isInner: false,
    getSrcFunc: (id: string) => `https://abc.xyz/file/${id}`,
    accept: 'image/jpeg, image/bmp, image/png, image/gif',
    uploadFunc: (_file: File) => 'fakeimg.ext',
    minWidth: undefined,
    maxWidth: undefined,
    minHeight: undefined,
    maxHeight: undefined,
    minSize: undefined,
    maxSize: undefined,
    validateImgFunc: (_width: number, _height: number, _size: number) => true
})

interface StateType {
    uploadedItems: any[]
    uploadingItems: any[]
}
const state = reactive<StateType>({
    uploadedItems: [],
    uploadingItems: [],
})

const headerList = computed(() => {
    return [
        { name: 'thumbnail', text: $t('AInputImage.Thumbnail'), width: 30 },
        { name: 'width', text: $t('AInputImage.Width'), width: 20 },
        { name: 'height', text: $t('AInputImage.Height'), width: 20 },
        { name: 'size', text: $t('AInputImage.Size'), width: 20 },
        { name: 'status', text: $t('AInputImage.Status'), width: 20 },
        { name: 'operations', text: $t('AInputImage.Operations'), width: 30 },
    ]
})
const statusList = computed(() => {
    return [
        $t('AInputImage.Uploading'),
        $t('AInputImage.Uploaded'),
        $t('AInputImage.UploadFailed'),
        $t('AInputImage.NonConformance')
    ]
})
const items = computed(() => {
    return [...state.uploadedItems, ...state.uploadingItems]
})
const canAdd = computed(() => {
    let rule = props.rules.find(a => a.name === 'maxLength') as Rule_maxLength | undefined
    if (!rule) {
        return true
    }
    return items.value.length <= rule.value
})

onMounted(() => {
    methods.refreshUpdatedItems()
})

watch(() => props.value, () => {
    methods.refreshUpdatedItems()
})

useInput(props)
const formItemHook = useFormItem(props)
const emit = defineEmits<{
    (e: 'update:value', value?: string[]): void
}>()
const inputRef = ref<HTMLInputElement>()

const methods = {
    input(val?: string[]) {
        emit('update:value', val)
    },
    openFileSelector() {
        inputRef.value!.click()
    },
    inputFile() {
        Array.from(inputRef.value!.files!).forEach(file => {
            if (!state.uploadingItems.some(a => a.file.name === file.name)) {
                let uploadingItem = {
                    id: `uploading_${file.name}`,
                    width: 0,
                    height: 0,
                    size: file.size,
                    status: 0,
                    file,
                    src: undefined,
                    valid: false
                }
                state.uploadingItems = [...state.uploadingItems, uploadingItem]
                methods.resolve(uploadingItem.id)
            }
        })
        inputRef.value!.value = ''
    },
    async resolve(itemId: string) {
        let item = methods.getItemById(itemId)
        if (!item) {
            return
        }
        let dataUrl = await utils.getDataUrlFromFile(item.file)
        item.src = dataUrl
    },
    async upload(itemId: string) {
        let item = methods.getItemById(itemId)
        if (!item) {
            return
        }
        if (item.valid) {
            item.status = 0
        } else {
            item.status = 3
            return
        }
        try {
            let id = await props.uploadFunc(item.file)
            methods.removeItem(itemId)
            let valueSet = new Set(props.value)
            valueSet.add(id)
            methods.input(Array.from(valueSet))
        } catch (error) {
            console.error(error)
            item.status = 2
        }
    },
    removeItem(itemId: string) {
        let item = methods.getItemById(itemId)
        if (!item) {
            return
        }
        if (item.file) {
            methods.removeUploadingItem(itemId)
        } else {
            methods.removeUploadedItem(itemId)
        }
    },
    removeUploadedItem(itemId: string) {
        methods.input((props.value ?? []).filter(a => `uploaded_${a}` !== itemId))
    },
    removeUploadingItem(itemId: string) {
        state.uploadingItems = state.uploadingItems.filter(a => a.id !== itemId)
    },
    refreshUpdatedItems() {
        state.uploadedItems = (props.value ?? []).map(a => {
            return {
                id: `uploaded_${a}`,
                width: 0,
                height: 0,
                size: 0,
                status: 1,
                src: props.getSrcFunc(a),
                value: a
            }
        })
    },
    setInfo(itemId: string, info: any) {
        let item = methods.getItemById(itemId)
        if (!item) {
            return
        }
        item.width = info.width
        item.height = info.height

        if (item.file) {
            item.valid = methods.isItemValid(item)
            methods.upload(itemId)
        }
    },
    getItemById(id: string) {
        return items.value.find(a => a.id === id)
    },
    getSizeStr(size: number) {
        if (!size) {
            return $t('AInputImageMulti.Unknown')
        }
        return utils.getStorageSizeStr(size)
    },
    isItemValid(item: any) {
        let { width, height, size } = item
        if (!width || !height || !size) {
            return false
        }
        return (props.minWidth === undefined || width >= props.minWidth)
            && (props.maxWidth === undefined || width <= props.maxWidth)
            && (props.minHeight === undefined || height >= props.minHeight)
            && (props.maxHeight === undefined || height <= props.maxHeight)
            && (props.minSize === undefined || size >= props.minSize)
            && (props.maxSize === undefined || size <= props.maxSize)
            && props.validateImgFunc(width, height, size)
    },
    onDragEnd(resortedItems: any[]) {
        let tempItems = resortedItems.filter(a => !a.file)
        methods.input(tempItems.map(a => a.value))
    },
}
</script>